#pragma once

/*--------------------------------------------------------------------------*/

struct EUniformType
{
	enum Type
	{
	    Float1 = 0,
	    Float2,
	    Float3,
	    Float4,
	    Float16,
	    Integer1,
	    Integer2,
	    Integer3,
	    Integer4,
	    UInteger1,
	    UInteger2,
	    UInteger3,
	    UInteger4
	};

	static const char* ToStr(Type type)
	{
		switch (type)
		{
#define MAKE_CASE(a) case a: return #a;
			MAKE_CASE(Float1)
			MAKE_CASE(Float2)
			MAKE_CASE(Float3)
			MAKE_CASE(Float4)
			MAKE_CASE(Float16)
			MAKE_CASE(Integer1)
			MAKE_CASE(Integer2)
			MAKE_CASE(Integer3)
			MAKE_CASE(Integer4)
			MAKE_CASE(UInteger1)
			MAKE_CASE(UInteger2)
			MAKE_CASE(UInteger3)
			MAKE_CASE(UInteger4)
#undef MAKE_CASE
		}

		AssertLogC(true, "Unknown EUniformType::Type in ToStr method!");
	}

};

/*--------------------------------------------------------------------------*/

struct GLUniformBuffer
{
	struct Uniform
	{
		uint		offset;
		uint		size;
		std::string			name;
		EUniformType::Type	type;

		Uniform() :
			offset(0),
			size(0),
			type(EUniformType::Float1)
		{}
	};

	bool changed;
	uint updateBeginByte;
	uint updateEndByte;

	uint handle;
	void* data;
	uint size;
	uint bindingIndex;

	const std::string name;
	std::string glslContent;
	std::vector<Uniform> memmap;

	GLUniformBuffer(const std::string name) :
		handle(0),
		data(NULL),
		size(0),
		bindingIndex(0),
		name(name),
		updateBeginByte(UINT_MAX),
		updateEndByte(0),
		changed(true)
	{}

	~GLUniformBuffer()
	{
		if (data)
		{
			delete [] data;
			data = NULL;
		}
	}
};

/*--------------------------------------------------------------------------*/

struct GLUniformBufferItemDesc
{
	std::string				name;
	EUniformType::Type		type;
};

struct GLUniformBufferDesc
{
	uint					name;
	std::string						nameStr;
	const GLUniformBufferItemDesc*	items;
};

/*--------------------------------------------------------------------------*/

class UniformWrapper
{
protected:
	UniformWrapper();
	~UniformWrapper();

	static int				_uniformBuffersCount;
	static uint		_nextUniformBuffersBindingIndex;
	static GLUniformBuffer* _boundUBO;
	static std::map<std::string, GLUniformBuffer*>	_uniformBuffersMap;

public:
	static GLUniformBuffer*		GenUniformBuffer(const GLUniformBufferDesc* uboDesc);
	static void					DeleteUniformBuffer(GLUniformBuffer** ubo);
	static void					AllocateUniformBufferMemory(GLUniformBuffer* ubo);
	static void					ForceUpdateUniformBufferMemory(GLUniformBuffer* ubo);

	static GLUniformBuffer*		GetUniformBuffer(const std::string& name);
	static void					BindUniformBuffer(GLUniformBuffer* ubo, bool force = false);

	static void					UpdateUniformBuffer(GLUniformBuffer* vb, bool force = false);
	static void					SetUniformBufferValue(GLUniformBuffer* vb, uint index, void* sdata);
};